﻿namespace HutongGames.PlayMaker
{
    using System;
    using System.Collections.Generic;
    using System.Reflection;

    public static class ReflectionUtils
    {
        public static bool CanReadMemberValue(MemberInfo member)
        {
            MemberTypes memberType = member.MemberType;
            if (memberType != MemberTypes.Field)
            {
                return ((memberType == MemberTypes.Property) && ((PropertyInfo) member).CanRead);
            }
            return true;
        }

        public static bool CanSetMemberValue(MemberInfo member)
        {
            MemberTypes memberType = member.MemberType;
            if (memberType != MemberTypes.Field)
            {
                return ((memberType == MemberTypes.Property) && ((PropertyInfo) member).CanWrite);
            }
            return true;
        }

        public static List<MemberInfo> GetFieldsAndProperties<T>(BindingFlags bindingAttr)
        {
            return GetFieldsAndProperties(typeof(T), bindingAttr);
        }

        public static List<MemberInfo> GetFieldsAndProperties(Type type, BindingFlags bindingAttr)
        {
            List<MemberInfo> list = new List<MemberInfo>();
            list.AddRange(type.GetFields(bindingAttr));
            list.AddRange(type.GetProperties(bindingAttr));
            return list;
        }

        public static Type GetMemberUnderlyingType(MemberInfo member)
        {
            switch (member.MemberType)
            {
                case MemberTypes.Event:
                    return ((EventInfo) member).EventHandlerType;

                case MemberTypes.Field:
                    return ((FieldInfo) member).FieldType;

                case MemberTypes.Property:
                    return ((PropertyInfo) member).PropertyType;
            }
            throw new ArgumentException("MemberInfo must be of type FieldInfo, PropertyInfo or EventInfo", "member");
        }

        public static object GetMemberValue(MemberInfo member, object target)
        {
            switch (member.MemberType)
            {
                case MemberTypes.Field:
                    return ((FieldInfo) member).GetValue(target);

                case MemberTypes.Property:
                    try
                    {
                        return ((PropertyInfo) member).GetValue(target, null);
                    }
                    catch (TargetParameterCountException exception)
                    {
                        throw new ArgumentException("MemberInfo has index parameters", "member", exception);
                    }
                    break;
            }
            throw new ArgumentException("MemberInfo is not of type FieldInfo or PropertyInfo", "member");
        }

        public static void SetMemberValue(MemberInfo member, object target, object value)
        {
            MemberTypes memberType = member.MemberType;
            if (memberType != MemberTypes.Field)
            {
                if (memberType != MemberTypes.Property)
                {
                    throw new ArgumentException("MemberInfo must be if type FieldInfo or PropertyInfo", "member");
                }
            }
            else
            {
                ((FieldInfo) member).SetValue(target, value);
                return;
            }
            ((PropertyInfo) member).SetValue(target, value, null);
        }
    }
}

